package com.gaofans.vertx.boot.web.handler;

import com.gaofans.vertx.boot.web.auth.AuthorityDefinition;
import com.gaofans.vertx.boot.web.route.RouteDefinition;
import com.gaofans.vertx.boot.web.route.RouterConfigurer;
import com.gaofans.vertx.boot.web.utils.UriUtil;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.AuthHandler;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.ResponseContentTypeHandler;
import io.vertx.ext.web.handler.TemplateHandler;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class RouterHandlerConfigurer implements RouterConfigurer, ApplicationContextAware {

    private HttpHandlerDiscoverer handlerDiscoverer;

    private ApplicationContext applicationContext;

    public RouterHandlerConfigurer(HttpHandlerDiscoverer handlerDiscoverer) {
        this.handlerDiscoverer = handlerDiscoverer;
    }

    @Override
    public void accept(RouteDefinition definition, Route route) {
        HttpHandlerChain httpHandlers = handlerDiscoverer.findHttpHandlers();
        route.path(definition.getPath()).order(definition.getOrder());
        for (HttpMethod httpMethod : definition.getHttpMethod()) {
            route.method(httpMethod);
        }
        for (String consume : definition.getConsumes()) {
            route.consumes(consume);
        }
        for (String produce : definition.getProduces()) {
            route.produces(produce);
        }
        for (HttpHandlerDefinition preHandler : httpHandlers.getPreHandlers()) {
            setHandler(definition, route, preHandler);
        }
        route.handler(definition.getHandler());
        for (HttpHandlerDefinition postHandler : httpHandlers.getPostHandlers()) {
            setHandler(definition, route, postHandler);
        }
        route.handler(routingContext -> {
            if(!routingContext.response().ended()){
                routingContext.response().end();
            }
        });
    }

    /**
     * 设置handler
     * @param definition
     * @param route
     * @param handler
     * @return
     */
    private void setHandler(RouteDefinition definition, Route route, HttpHandlerDefinition handler) {

        if(UriUtil.matchOneOf(handler.getPath(),route.getPath()) && !checkInArray(definition.getExcludeHandlers(),handler.getHandler())){
            addHandler(definition, route, handler);
        }else if(checkInArray(definition.getIncludeHandlers(),handler.getHandler())){
            addHandler(definition, route, handler);
        }
    }

    /**
     * 加载并设置
     * @param definition
     * @param route
     * @param handler
     */
    private void addHandler(RouteDefinition definition, Route route, HttpHandlerDefinition handler) {
        Handler<RoutingContext> h = this.applicationContext.getBean(handler.getName(),handler.getHandler().getClass());
        AuthorityDefinition authorityDefinition = definition.getAuthorityDefinition();
        if(authorityDefinition != null){
            if(h instanceof AuthHandler && handler.getPosition().equals(authorityDefinition.getPosition())){
                for (String authority : definition.getAuthorityDefinition().getAuthorities()) {
                    ((AuthHandler)handler).addAuthority(authority);
                }
            }
        }
        //判断是否注入bodyHandler
        if(!definition.isRequestBody() && h instanceof BodyHandler){
            return;
        }
        //判断是否注入TemplateHandler
        if(h instanceof TemplateHandler){
            if(definition.isTemplateRender()){
                route.handler(h);
            }
            return;
        }
        //判断是否注入ResponseBodyHandler
        if(h instanceof ResponseContentTypeHandler){
            if(definition.isResponseBody()){
                route.handler(h);
            }
            return;
        }
        route.handler(h);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private boolean checkInArray(Class<? extends Handler<RoutingContext>>[] handlers,Handler<RoutingContext> handler){
        for (Class<? extends Handler<RoutingContext>> aClass : handlers) {
            if(aClass.equals(handler.getClass())){
                return true;
            }
        }
        return false;
    }


}
